#pragma once

#include <iostream>
#include <string>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <functional>
#include <unordered_map>

#include "log.hpp"
#include "Nocopy.hpp"
#include "Comm.hpp"
#include "InetAddr.hpp"
#include "Thread.hpp"
#include "ThreadPool.hpp"
#include "Task.hpp"

static const int default_backlog = 5;

using task_t = std::function<void()>;

// 客户端提供的回调函数
using callback_t = std::function<void(int, InetAddr &addr)>;

// 前置声明先
class TcpServer;

class ThreadData
{
private:
    int _sockfd;
    TcpServer *_tcpserver;
    InetAddr _InetAddr;

    

public:
    ThreadData(int sockfd, TcpServer *tcpserver, struct sockaddr_in &addr_in) : _sockfd(sockfd), _tcpserver(tcpserver), _InetAddr(addr_in)
    {
    }
    ~ThreadData()
    {
        // 析构自动关闭文件描述符
        close(_sockfd);
    }

    int get_sockfd()
    {
        return _sockfd;
    }

    TcpServer *get_tcp_server()
    {
        return _tcpserver;
    }

    InetAddr &get_addr()
    {
        return _InetAddr;
    }
};

class TcpServer : public nocopy
{
private:
    uint16_t _port;
    int _listen_sockfd;
    bool _isrunning;

    std::unordered_map<std::string, callback_t> funcs;
public:
    TcpServer(uint16_t port) : _port(port)
    {
    }
    ~TcpServer()
    {
        close(_listen_sockfd);
    }

    void Init()
    {
        // 1. 创建套接字
        _listen_sockfd = socket(AF_INET, SOCK_STREAM, 0);
        // AF_INET  ->  IPV4
        // SOCK_STREAM  ->  TCP 协议

        if (_listen_sockfd < 0)
        {
            lg.LogMessage(Fatal, "Create socket error!,errno code: %d,errno string: %s\n", errno, strerror(errno));
            exit(Fatal);
        }
        lg.LogMessage(Debug, "Create socket success,sockfd: %d\n", _listen_sockfd);

        //  解决服务端在主动关闭时,再次启动绑定失败的问题
        int opt = 1; // SOL_SOCKET 在 socket 层
        setsockopt(_listen_sockfd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof opt);

        // 2.填充本地网络信息
        struct sockaddr_in local;
        bzero(&local, sizeof local);
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = htonl(INADDR_ANY); // 0 ???

        // bind

        if (bind(_listen_sockfd, (const sockaddr *)&local, (socklen_t)sizeof local) != 0)
        {
            lg.LogMessage(Fatal, "Bind error!,errno code : %d , error string : %s \n", errno, strerror(errno));
            exit(Fatal);
        }

        lg.LogMessage(Debug, "bind socket success, sockfd: %d\n", _listen_sockfd);

        // Tcp 特有
        if (listen(_listen_sockfd, default_backlog) != 0)
        {
            lg.LogMessage(Fatal, "listen Error!,errno code : %d , error string : %s\n", errno, strerror(errno));
            exit(Listen_Err);
        }
        lg.LogMessage(Debug, "listen socket success, sockfd: %d\n", _listen_sockfd);

        // 创建线程池
        ThreadNs::ThreadPool<task_t>::GetInstance()->Start();
    }

    static void *HandlerRequest(void *arg)
    {

        // 设置线程分离，那么主线程就无需阻塞等待，线程结束自动回收
        pthread_detach(pthread_self());

        ThreadData *data = static_cast<ThreadData *>(arg);

        // 本函数时静态成员函数，没有this指针，所以无法直接调用成员函数
        // Service(data->get_sockfd());

        // 将 TcpSerevr 封装入函数中
        data->get_tcp_server()->Service(data->get_sockfd(), data->get_addr());

        // close(data->get_sockfd());
        //  关闭文件描述符的操作就放在析构函数中

        delete data;

        return nullptr;
    }

    void Start()
    {
        _isrunning = 1;

        while (_isrunning)
        {
            // accept 返回一个文件描述符
            struct sockaddr_in peer; // 接收一个 sockaddr_in
            socklen_t len = sizeof peer;
            int sockfd = accept(_listen_sockfd, (struct sockaddr *)&peer, &len);

            if (sockfd < 0)
            {
                lg.LogMessage(Warning, "accept socket error,error code: %d , error string : %s\n", sockfd, strerror(errno));

                // Tcp 接收失败不会直接退出，继续接收下一个即可
                continue;
            }

            lg.LogMessage(Debug, "Create socketfd : %d \n", sockfd);

            // 当前代码是单一的，accept 接收到文件描述符之后，直接进入到 Service , 此时除非这个 sockfd 的通信结束了，否则就永远不可能退出来接收其他进程的信息
            // Service(sockfd);
            // close(sockfd);

            // // 多进程版__1 ,可以让多个用户连接

            // pid_t id = fork();
            // if (id < 0)
            // {
            //     return;
            // }
            // else if (id == 0)
            // {
            //     // child

            //     //  子进程不需要连接 _listen_socket
            //     close(_listen_sockfd);

            //     // 让子进程再创建一个子进程，然后子进程自己退出，这样孙子进程就为孤儿进程，被系统领养，推出后直接被回收，不需要父进程再阻塞等待了
            //     if(fork()>0)exit(0);

            //     Service(sockfd);
            //     close(sockfd);  // 服务后关闭文件描述符

            //     exit(0);
            // }
            // else
            // {
            //     // 父进程不需要连接 sockfd
            //     // waitpid(-1,nullptr,0)  // 等待任意一个子进程的退出
            //     close(sockfd);
            // }

            // // 不想使用 子进程再创建子进程的方式 让父进程避免等待
            // // 使用信号的方式处理  SIGCHILD SIG_IGN

            // signal(SIGCHLD, SIG_IGN); // 对 SIGCHLD 进行忽略,那么子进程在退出时自动释放资源

            // // 多进程版__2 ,可以让多个用户连接
            // pid_t id = fork();
            // if (id < 0)
            // {
            //     close(sockfd); // 创建错误，关闭子进程通信的文件描述符
            //     continue;
            // }
            // else if (id == 0)
            // {
            //     // child

            //     //  子进程不需要连接 _listen_socket
            //     close(_listen_sockfd);

            //     // 让子进程再创建一个子进程，然后子进程自己退出，这样孙子进程就为孤儿进程，被系统领养，推出后直接被回收，不需要父进程再阻塞等待了
            //     // if(fork()>0)exit(0);
            //     // 这里使用 signal(SIGCHLD,SIG_IGN);

            //     Service(sockfd);
            //     close(sockfd); // 服务后关闭文件描述符

            //     exit(0);
            // }
            // else
            // {
            //     // 父进程不需要连接 sockfd
            //     // waitpid(-1,nullptr,0)  // 等待任意一个子进程的退出
            //     close(sockfd);
            // }

            // pthread_t tid;
            //  这里必须用动态分配的方式，否则创建线程一旦结束，函数停止，data 就会被释放，新线程就无法有效读取到socket了
            //    ThreadData *data = new ThreadData(sockfd, this, peer);
            //    pthread_create(&tid, nullptr, HandlerRequest, data);

            // 线程池 版本

            task_t t = std::bind(&TcpServer::Service, this, sockfd, InetAddr(peer));
            ThreadNs::ThreadPool<task_t>::GetInstance()->Push(t);
        }
    }

    void RegisterFunc(const std::string &name, callback_t func)
    {
        // 将回调函数和函数名相对应
        funcs[name]=func;
    }

    void Routine()
    {
        
    }

    void Service(int sockfd, InetAddr _inetaddr) // const 修饰的类对象，智能调用类的const函数
    {
        int size = 1024;
        while (true)
        {
            char buffer[size];
            ssize_t n = read(sockfd, buffer, sizeof buffer - 1);
            if (n > 0)
            {
                buffer[n] = 0;

                std::string message = '[' + _inetaddr.PrintDebug() + "] -> " + buffer;
                std::cout << message << std::endl;
                write(sockfd, message.c_str(), message.size());
                // lg.LogMessage(Debug, "recive a message \n");
            }
            else if (n == 0)
            {
                lg.LogMessage(Info, "Client qiut ....\n");
                break; // Client 退出，此时服务端也同时退出
            }
            else
            {
                lg.LogMessage(Error, "Read sockfd error! error code: %d , error string : %s\n", sockfd, strerror(errno));
                break;
            }
        }

        close(sockfd);
    }
};